<?php defined('SYSPATH') or die('No direct script access.');

class Model_Model extends ORM
{ 
    const MODELS_PER_PAGE = 20;
    protected $_reload_on_wakeup = false;
    protected $_possible_models_table = 'possible_models';
    
    public function get_available_scores()
    {
        return array(
            5 => array(
                'name' => 'Отлично',
                'from' => 82, 'to' => 100
            ),
            4 => array(
                'name' => 'Хорошо',
                'from' => 64, 'to' => 81
            ),
            3 => array(
                'name' => 'Можно и лучше',
                'from' => 30, 'to' => 63
            ),
            2 => array(
                'name' => 'Не покупайте это',
                'from' => 1,  'to' => 29
            ),
        );
    }
    
    public function get_models_for_main()
    {
        if ($models = Cache::instance()->get('main_models')) {
            return $models;
        }
        
        $models = array();
        $categories = ORM::factory('category')->get_main_categories();
        foreach ($categories as $top_category) {
            foreach ($top_category['items'] as $category) {
                $top = ORM::factory('model')
                            ->where('category_id', '=', $category['id'])
                            ->order_by('reviews_score', 'DESC')
                            ->limit(3)
                            ->find_all()
                            ->as_array(); 
                $models[$category['id']] = $top;  
            }    
        }
        
        Cache::instance()->set('main_models', $models);
        return $models;    
    }
    
    // ---
    public function get_cameras_for_main($limit = 2)
    {
        // TODO cache
        $types    = Model_Specs_Camera::$_camera_types;
        $type_ids = array();
        foreach ($types as $type => $name) {
            $type_ids[] = $type;    
        }
        
        $limit_ids = array();
        for ($i = 1; $i <= $limit; $i++) {
            $limit_ids[] = $i;    
        }
        
        $specs = ORM::factory('specs_camera')
                    ->join('models')
                    ->on('models.id', '=', 'specs_camera.id')
                    ->where('specs_camera.camera_type', 'IN', $type_ids)
                    ->and_where('models.same_type_score', 'IN', $limit_ids)
                    ->find_all();
        
        $model_ids = array();
        foreach ($specs as $spec) {
            $model_ids[] = $spec->id;   
        }
        
        $cameras = ORM::factory('model')
                    ->where('id', 'IN', $model_ids)
                    ->find_all();
                    
        $result = array();
        $specs  = Model_Main::process_array($specs);
        $cameras = Model_Main::process_array($cameras);    
        foreach ($types as $type => $name) {
            $result[$type] = array(); 
            foreach ($specs as $spec) {
                if ($spec->camera_type == $type) {
                    $result[$type][] = array(
                        'specs'  => $spec,
                        'camera' => $cameras[$spec->id],
                    );    
                }    
            }
             
        }     
        return $result;
    }
    
    public function get_cameras_by_type($type, $page = 1)
    {
        // TODO cache 
        if (!array_key_exists($type, Model_Specs_Camera::$_camera_types)) {
            throw new Model_Exception('Данного типа не существует');
        }
        $limit  = self::get_models_per_page();
        $offset = ($page - 1) * $limit;
        $specs = ORM::factory('specs_camera')
                    ->join('models')
                    ->on('models.id', '=', 'specs_camera.id')
                    ->where('specs_camera.camera_type', '=', $type)
                    ->order_by('models.same_type_score', 'ASC')
                    ->offset($offset)
                    ->limit($limit)
                    ->find_all();
        
        $model_ids = array();
        foreach ($specs as $spec) {
            $model_ids[] = $spec->id;   
        }
        
        $cameras = ORM::factory('model')
                    ->where('id', 'IN', $model_ids)
                    ->find_all();
                    
        $result = array();
        $specs  = Model_Main::process_array($specs);
        $cameras = Model_Main::process_array($cameras);    
        foreach ($specs as $spec) {
            $result[] = array(
                'specs'  => $spec,
                'camera' => $cameras[$spec->id],
            );       
        }
    
        return $result;
    }
    
    public function get_cameras_for_compare()
    {
        // TODO cache
        $cameras = array(
            2 => array(
                array(
                    0 => 3251,
                    1 => 3136,
                ),
                array(
                    0 => 3041,
                    1 => 3136,
                ),
                array(
                    0 => 3041,
                    1 => 3252,
                ),
                array(
                    0 => 3041,
                    1 => 3251,
                ),
            ),
            'cross' => array(
                array(
                    0 => 3252,
                    1 => 3024,
                ),
                array(
                    0 => 3041,
                    1 => 3024,
                ),
                array(
                    0 => 3024,
                    1 => 3092,
                ),
                array(
                    0 => 3011,
                    1 => 3092,
                ),
            ),
            3 => array(
                array(
                    0 => 3024,
                    1 => 3011,
                ),
                array(
                    0 => 54616,
                    1 => 54089,
                ),
                array(
                    0 => 54616,
                    1 => 3011,
                ),
                array(
                    0 => 54616,
                    1 => 54094,
                ),
                array(
                    0 => 54094,
                    1 => 5336,
                ),
                array(
                    0 => 3216,
                    1 => 54616,
                ),
            ),
            6 => array(
                array(
                    0 => 54166,
                    1 => 3186,
                ),
            ),
            8 => array(
                array(
                    0 => 3107,
                    1 => 54262,
                ),
            ),
            0 => array(
                array(
                    0 => 54113,
                    1 => 54117,
                ),
            ),
            1 => array(
                array(
                    0 => 3220,
                    1 => 5499,
                ),
                array(
                    0 => 54626,
                    1 => 3182,
                ),
                array(
                    0 => 3234,
                    1 => 3074,
                ),
            ),
            
        );
        
        $all = array();
        foreach ($cameras as $items) {
            foreach ($items as $item) {
                if (!isset($all[$item[0]])) {
                    $all[$item[0]] = $item[0];   
                } 
                if (!isset($all[$item[1]])) {
                    $all[$item[1]] = $item[1];   
                }   
            }    
        }  
        
        $all = ORM::factory('model')
                    ->where('id', 'IN', $all)
                    ->find_all();
        $all = Model_Main::process_array($all); 
                    
        foreach ($cameras as &$items) {
            foreach ($items as &$item) {
                $item[0] = $all[$item[0]];
                $item[1] = $all[$item[1]];  
            }    
        }
                    
        return $cameras;
    }
    
    public function get_reviews_score()
    {
        $positive = $this->count_four_stars + 2*$this->count_five_stars;
        // Единица без коэффициента, т.к. скорее всего люди погорячились =)
        $negative = $this->count_three_stars + 2*$this->count_two_stars + $this->count_one_stars;
        
        $total = $positive + $negative;
        if ($total == 0) {
            return 0;
        }
        
        return round(((($positive + 1.9208) / ($positive + $negative) - 
            1.96 * sqrt(($positive * $negative) / ($positive + $negative) + 0.9604) / 
            ($positive + $negative)) / (1 + 3.8416 / ($positive + $negative))) * 10000); 
    }
    
    public function get_models($category = null, $brand = null, $price = null, $score = null, $page = null, $count = false)
    {        
        $category_id = $category;
        if ($category instanceof Model_Category) {
            $category_id = $category->id;
            $categories  = null;
            if ($category->childs) {
                $categories = explode(',', $category->childs); 
            } 
        }
        
        $brand_id = $brand;
        if ($brand instanceof Model_Brand) {
            $brand_id = $brand->id;    
        }
        
        $from_price = $to_price = null;
        if (isset($price['from']) && $price['from']) {
            $from_price = $price['from'];    
        }
        if (isset($price['to']) && $price['to']) {
            $to_price = $price['to'];    
        }
        
        $from_score = $to_score = null;
        $scores = ORM::factory('model')->get_available_scores();
        foreach ($scores as $mark => $item) {
            if ($score == $mark) {
                $from_score = $item['from']; 
                $to_score   = $item['to'];        
            }    
        }
        
        if ($models = Cache::instance()->get('models_'.$category_id.'_'.$brand_id.'_'.$from_price.'_'.$to_price.'_'.$score.'_'.$page.'_'.$count)) {
            return $models;
        }
         
        $query = ORM::factory('model'); 
        
        if ($category_id) {
            if ($categories) {
                $query->where('category_id', 'IN', $categories);    
            } else {
                $query->where('category_id', '=', $category_id);
            }
        }
        
        if ($brand_id) {
            $query->where('brand_id', '=', $brand_id);    
        }
        
        if ($from_price) {
            $query->where('max_price', '>=', $from_price);    
        }
        if ($to_price) {
            $query->where('min_price', '<=', $to_price); 
            if (!$from_price) {
                $query->where('max_price', '!=', 0);    
            }   
        }
        
        if ($from_score) {
            $query->where('score', '>=', $from_score);    
        } 
        if ($to_score) {
            $query->where('score', '<=', $to_score);    
        }
        
        if ($count) {
            $result = $query->count_all();
            Cache::instance()->set('models_'.$category_id.'_'.$brand_id.'_'.$from_price.'_'.$to_price.'_'.$score.'_'.$page.'_'.$count, $result);
            return $result;
        }        
            
        if ($page) {
            $count = self::get_models_per_page();
            $offset = ($page - 1) * $count;
            $query->offset($offset)->limit($count);    
        }
        
        $models = $query->order_by('score', 'DESC')->find_all();  
        Cache::instance()->set('models_'.$category_id.'_'.$brand_id.'_'.$from_price.'_'.$to_price.'_'.$score.'_'.$page.'_'.$count,
                        Model_Main::get_array($models)); 
        return $models;
    }    
    
    public function get_related_models(Model_Model $model, $count = 8)
    {
        return ORM::factory('model')
            ->where('category_id', '=', $model->category_id)
            ->and_where('id', '!=', $model->id)
            ->order_by(DB::expr('RAND()'))
            ->limit($count)
            ->find_all();
    }
    
    public function get_random_models($count = 7)
    {
        return ORM::factory('model')
            ->where('id', '<', 7305)
            ->order_by(DB::expr('RAND()'))
            ->limit($count)
            ->find_all();
    }
    
    public static function get_models_per_page()
    {
        return self::MODELS_PER_PAGE;
    }
    
    /**
    * put your comment there...
    * 
    */
    public function add_model($name, $market_url)
    {
        if (!($name = trim($name))) {
            //throw new Model_Exception('Название не может быть пустым');
        }
        
        $this->name = $name;
        $this->save();
        
        // Парсим отзывы
        Model_ReviewParser_YandexMarket::instance()->parse($this, $market_url);
        
        return $this;  
    }
    
    /**
    * Кривоватым способом вычисляем, к какой модели относится данный товар
    * 
    * @param Model_Product $product
    */
    public function calculate_model(Model_Product $product)
    {
        // по названию ищем похожие товары
        // составляем все возможные варианты идентификатора модели с количеством повторений в результатах
        // складываем релевантности
        // делим сумму релевантностей на количество результатов с данной моделью
        // если результат > 0.5(????), то берем больший, если нет, то на модерацию
        
        if (!$product->name) {
            throw new Model_Exception('Имя товара должно быть задано');
        }
        
        $sql = DB::query(Database::SELECT, "SELECT model_id, url, name, MATCH (clean_name) AGAINST (:clean_name) AS relevance FROM products
                                            WHERE MATCH (clean_name) AGAINST (:clean_name)
                                                AND id != :id
                                            LIMIT 20");
        $products = $sql->param(':clean_name', $product->clean_name)
                        ->param(':id', $product->id)
                        ->execute()->as_array();
        
        /*echo '<pre>';
        print_r($products);
        echo '</pre>';*/
        
        $models = array();
        foreach ($products as $_product) {
            if (!isset($models[$_product['model_id']])) {
                $models[$_product['model_id']] = array(
                    'model_id' => $_product['model_id'], 
                    'count' => 1,
                    'relevance' => $_product['relevance'],
                    'result' => 0,
                );    
            } else {
                $models[$_product['model_id']]['count']++;
                $models[$_product['model_id']]['relevance'] += $_product['relevance'];
            }      
        }
        
        $model_id = null;
        /*if (count($models)) {
            $max = $models[0];   // !!!!! Неправильно
            foreach ($models as $_id => $_model) {
                $result = $_model['relevance'] / $_model['count'];
                if ($result > $max['result']) {
                    $_model['result'] = $result;
                    $max = $_model;    
                }     
            }
            if ($max > 0.5) {
                $model_id = $max['model_id']; 
            }          
        }*/
        
        if (!$model_id) {
            $this->_add_potential_models($product, $models);  
            return null;     
        } 
        
        return ORM::factory('model', $model_id);
    }
    
    public function find_models($name)
    {
        $models = ORM::factory('model')
            ->where('name', 'LIKE', '%'.$name.'%')
            ->find_all(); 
            
        if (count($models)) {
            return $models[0];
        }
        return null;
    }
    
    protected function _add_potential_models(Model_Product $product, $models)
    {
        // Если не удалось определить, запоминаем 3 самых релевантных варианта модели
        // Перегоняем массив моделей - model_id => relevance
        // Сортируем
        if (count($models)) {
            $potential_models = array();
            $i = 0;
            foreach ($models as $_id => $_model) {
                if ($i > 2) {
                    break;
                }
                $relevance = $_model['relevance'];
                $potential_models[$_id] = $relevance; 
                $i++;  
            }
            asort($potential_models);
        
            foreach ($potential_models as $_id => $_relevance) {
                if ($_id) {
                    // TODO check unique
                    $model_potential = ORM::factory('model_potential', array('product_id' => $product->id, 'model_id' => $_id)); 
                    if (!$model_potential->loaded()) {
                        $model_potential = ORM::factory('model_potential');
                        $model_potential->product_id = $product->id;
                        $model_potential->model_id   = $_id;
                        $model_potential->relevance  = $_relevance;
                        $model_potential->save();
                    }
                }
            }
            
            return true;
        }  
        return false;      
    }
    
    public function get_potential_models(Model_Product $product)
    {
        // TODO может переписать с кешем в статичную переменную
        $potential_models = ORM::factory('model_potential')
                    ->where('product_id', '=', $product->id)
                    ->order_by('relevance', 'DESC')
                    ->find_all();
        
        $models = array();
        foreach ($potential_models as $model_potential) {
            $models[] = ORM::factory('model', $model_potential->model_id);    
        }
        return $models;       
    }
    
    public function delete_potential_models(Model_Product $product)
    {
        // TODO может переписать с кешем в статичную переменную
        DB::delete('model_potential')
          ->where('product_id', '=', $product)
          ->execute($this->_db);   
    }
    
    public function set_market_url($url)
    {
        // TODO validation
        
        $this->market_url = $url;
        $this->save();
    }
    
    public function set_rozetka_urls(array $urls)
    {
        // TODO validation
        
        $urls = implode("\n", $urls);        
        $this->rozetka_urls = $urls;
        $this->save();
    }
    
    public function set_otzovik_urls(array $urls)
    {
        // TODO validation
        
        $urls = implode("\n", $urls);        
        $this->otzovik_urls = $urls;
        $this->save();
    }
    
    public function change_count_reviews()
    {
        //$column_name = ($entity ? $entity . '_' : '') . 'count_reviews';
		$column_name = 'count_reviews';
        $this->set($column_name, $this->__get($column_name) + 1); 
        $this->save(); 
    }
    
    public function change_count_stars($rating)
    {        
        if (!$rating) {
            return false;
        }   
        $stars = array(
            1 => 'one',
            2 => 'two',
            3 => 'three',
            4 => 'four',
            5 => 'five',
        );
        if (!array_key_exists($rating, $stars)) {
            throw new Model_Exception('Неверный рейтинг');
        }
        $star = $stars[$rating];
        //$column_name = ($entity ? $entity . '_' : '') . 'count_' . $star . '_stars';
		$column_name = 'count_' . $star . '_stars';
        $this->set($column_name, $this->__get($column_name) + 1);
        $this->save();
    }
    
    public function get_rating()
    {
        $one   = $this->count_one_stars;
        $two   = $this->count_two_stars;
        $three = $this->count_three_stars;
        $four  = $this->count_four_stars;
        $five  = $this->count_five_stars;
        
        $count = $one + $two + $three + $four + $five;
        $rating = 0;
        if ($count) {
            $rating = ($one + 2*$two + 3*$three + 4*$four + 5*$five) / $count;
        } 
        return array(
            'count'  => $count,
            'rating' => round($rating, 2),
        ); 
    }
    
    public function extract_models($products)
    {
        if ($products instanceof Model_Product) {
            $product = $products;
            $models = array();
            if ($product->model_id) {
                $models[$product->model_id] = ORM::factory('model', $product->model_id);
            }  
        } else if (is_array(Model_Main::get_array($products))) {
            $models = array();
            foreach ($products as $product) {
                if (!in_array($product->model_id, $models) && $product->model_id) {
                    $models[] = $product->model_id;
                }    
            }
            if (count($models)) {            
                $models = ORM::factory('model')->where('id', 'IN', $models)->find_all();
                $models = Model_Main::process_array($models); 
            }            
        }
        return $models;
    }
    
    public function simple_search($name)
    {
        return ORM::factory('model')->where('name', 'LIKE', trim($name))->find_all();
    }
    
    public function search($query, $limit = null)
    {
        if(!mb_strlen($query)) {
            return null;
        }   
        $result = ORM::factory('model')
                    ->where(DB::expr('MATCH(name)'), '', DB::expr('AGAINST('.$this->_db->escape($query).')'));
        if ($limit) {
            $result->limit($limit);     
        }
        return $result->find_all();
    }
    
    /**
    * Обработать товары магазина и соотнести в модели что возможно
    * 
    * @param Model_Shop $shop
    */
    public function process_shop(Model_Shop $shop)
    {       
        // Цикл по товарам категрии
        // Если обработанное название совпадает с какой-нибудь сущствющей моделью, то крепи товар к этой модели
        // Если не совпадает, то пробуем спарсить ЯМ и также проверить полученную модель Маркета на корректность (обработанные названия эквивалентны) 
        $count_found = $count_market = 0;
        
        $category_id = 69;
        $products = ORM::factory('product')->get_products($category_id, 0); 
        $count = 0;
        foreach ($products as $product) {
            $count++;
            
            if ($count > 3) {
                //break;
            }
            echo $count.' - '.$product->name . " ----- " . $product->get_clean_name();
            
            $model = ORM::factory('model')->find_models($product->name);
            //if ($model && ($product->get_clean_name() == $model->get_clean_name())) {
            if ($model && (strstr($product->get_clean_name(), $model->get_clean_name()) !== false ||
                        strstr($model->get_clean_name(), $product->get_clean_name()) !== false)) {
                $product->to_model($model);
                $count_found++;
                echo '<span style="color:red;">В '.$model->name.'</span><br />';
                continue;        
            }
            
            sleep(mt_rand(1, 2));   // Не ддосим Маркет 
            // Нашей модели не нашлось, пробуем искать в Маркете
            $market_url = Model_ReviewParser_YandexMarket::instance()->get_reviews_url($product->clean_name);
            $model_name = null;
            if ($market_url) {
                sleep(mt_rand(0, 1));
                $model_name = Model_ReviewParser_YandexMarket::instance()->get_model_name($market_url);
                echo '<span style="color:green;">Пробуем '.$model_name.' - '.$market_url.'</span>';
                if ($model_name && ($product->get_clean_name() == Model_Main::get_clean_name($model_name))) {
                    // Создаем модель
                    $model = ORM::factory('model')->add_model($model_name, $market_url);
                    $product->to_model($model); 
                    $count_market++;
                    echo ' -- <span style="color:green;">В '.$model_name.'</span><br />';
                    continue;  
                }
            }  
            echo '<br />';
        }
        echo '<br /><br />Всего - '.count($products).'<br />'.
            'Нашлось наших моделей - '.$count_found.'<br />'.
            'Добавили из Маркета - '.$count_market.'<br />';
        
    }
    
    public function process_shop2(Model_Shop $shop)
    {               
        $category_id = 10;
        $products = ORM::factory('product')->get_products($category_id, 0); 
        $count = $count_found = 0;
        foreach ($products as $product) {
            // Пытаемся получить потенциальные модели снова (после добавления товаров),
            // т.к. во время ручного соотношения по моделям уже могли появиться нужные варианты 
            ORM::factory('model')->calculate_model($product);
            
            $count++;
            
            if ($count > 3) {
                //break;
            }
            echo $count.' ('.$product->id.') - '.$product->name . " ----- " . $product->get_clean_name();
            
            $models = ORM::factory('model')->get_potential_models($product);
            foreach ($models as $model) {
                echo '<span style="color:green;">'.$model->name.'</span>';
                if ((strstr($product->get_clean_name(), $model->get_clean_name()) !== false ||
                        strstr($model->get_clean_name(), $product->get_clean_name()) !== false)) {
                    $product->to_model($model);
                    $count_found++;
                    echo '<span style="color:red;">В '.$model->name.'</span><br />';
                    break;        
                }    
            }
            echo '<br />'; 
        }
        echo '<br /><br />Всего - '.count($products).'<br />'.
            'Нашлось наших моделей - '.$count_found.'<br />';
        
    }
    
    public function get_clean_name()
    {
        return Model_Main::get_clean_name($this->name);
    }
    
    public function get_image()
    {
        $url   = Model_Main::get_static_host_url();      
        $image = '/uploads/models2/'.sprintf('%02x/%d', $this->id % 0xff, $this->id).'/main.jpg';
        if (!Model_Main::is_production_host()) {
            return $url.$image;        
        }
        if (file_exists(APPPATH.'../media'.$image)) {
            return $url.$image;
        }
        return '/media/img/no-image.jpg';
    }
    
    public function get_small_image()
    {
        $url   = Model_Main::get_static_host_url();      
        $image = '/uploads/models2/'.sprintf('%02x/%d', $this->id % 0xff, $this->id).'/smain.jpg';
        if (!Model_Main::is_production_host()) {
            return $url.$image;        
        }
        if (file_exists(APPPATH.'../media'.$image)) {
            return $url.$image;
        }
        return '/media/img/no-image.jpg';   
    }
    
    public function get_images()
    {
        $images = array();
        if (!Model_Main::is_production_host()) {
            return $images;        
        }
        $url    = 'http://static.buzilio.ru';
        $path   = '/uploads/models2/'.sprintf('%02x/%d', $this->id % 0xff, $this->id);
        $dir    = APPPATH.'../media'.$path;
        if ($handle = opendir($dir)) {
            while (false !== ($file = readdir($handle))) {
                if ($file == '.' || $file == '..' || $file == 'smain.jpg' || $file == 'main.jpg') {
                    continue;    
                }
                $images[] = $url.$path.'/'.$file; 
            }
        }       
        return $images;
    }
}
